package com.siecom.module;

import android.os.Build;
import android.util.Pair;

import com.TcFingerClient.TcFingerClientApi;
import com.siecom.constconfig.ErrorDefine;
import com.siecom.tool.ByteTool;
import com.siecom.tool.LogUtil;
import com.siecom.tool.Timer;

import java.util.Arrays;

/**
 * Created by zhq on 2016/10/20.
 */
public class WellFingerprintModule extends SerialPortModule {
    private static final String TAG = "WellFinger";
    private boolean isOpen;

    private static WellFingerprintModule instance;
    private static final int     baudRate1 = 9600;
    private static final int     baudRate2 = 115200;
    private static final String  serialDev = "/dev/ttyMT1";
    private static final byte    HEADER = 0x02;
    private static final byte    END    = 0x03;
    private static final byte[] revBuffer = new byte[40960];
    /**
     * 是否使用拆分数据的方式收发数据
     * 测试：必须使用过拆分数据
     */
    private static final boolean isUseSplitData = true;

    public static WellFingerprintModule getInstance() {
        if (null == instance) {
            synchronized (WellFingerprintModule.class) {
                if (null == instance) instance = new WellFingerprintModule();
            }
        }
        return instance;
    }

    public void powerOn(boolean isOpen) {
        int mode = isOpen ? 1 : 0;
        if (Build.MODEL.equals("A108Plus") && TcFingerClientApi.gpio_read(100) != isOpen)
            TcFingerClientApi.gpio_control(100, mode);
        if (TcFingerClientApi.gpio_read(124) != isOpen) {
            TcFingerClientApi.gpio_control(124, mode);
            try {
                Thread.sleep(1500); // 测试 1000ms 维尔指纹仪上电时间不够
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public boolean init() {
        powerOn(true);
        if (initSerialPort(serialDev, baudRate1)) {
            LogUtil.i(TAG,"维尔指纹串口打开成功");
            isOpen = true;
            return true;
        }
        return false;
    }

    public synchronized int startFeatureTask(int timeout, byte[] feature, int[] featureLen) {
        setStopRun(false);
        Timer timer = new Timer(timeout);
        timer.start();
        int ret = 0;
        while (!isStopRun()) {
            if (timer.timeOut()) {
                setStopRun(true);
                return ErrorDefine.TIME_OUT;
            }
            ret = checkFinger();
            if (ret != 0) continue;
            ret = getFeature(timeout, feature, featureLen);
            LogUtil.i(TAG,"获取维尔指纹特征码结果：ret = " + ret);
            if (ret == 0) {
                return ret;

            }
        }
        return ret;
    }
    public synchronized Pair<Integer, byte[]> startTask(int timeout, byte[] feature, int[] featureLen) {
        setStopRun(false);
        Timer timer = new Timer(timeout);
        timer.start();
        int ret = 0;
        while (!isStopRun()) {
            if (timer.timeOut()) {
                setStopRun(true);
                return new Pair<>(ErrorDefine.TIME_OUT, null);
            }
            ret = checkFinger();
            if (ret != 0) continue;
            ret = getFeature(timeout, feature, featureLen);
            LogUtil.i(TAG,"获取维尔指纹特征码结果：ret = " + ret);
            if (ret == 0) {
                while (!isStopRun()) {
                    if (timer.timeOut()) {
                        setStopRun(true);
                        return new Pair<>(ErrorDefine.TIME_OUT, null);
                    }
                    Pair<Integer, byte[]> pair = getImage();
                    ret = pair.first;
                    LogUtil.i(TAG,"获取维尔指纹图像结果：ret = " + ret);
                    if (ret == 0) {
                        setStopRun(true);
                        timer.setTimOut();
                        return pair;
                    }
                }
            }
        }
        return new Pair<>(ret, null);
    }

    public String getVersion() {
        byte[] send = new byte[0];
        flush();
        sendPacket(send, 0, (byte) 0x09, (byte) 0, (byte) 0, (byte) 0);
        int[] len = new int[1];
        int ret = revPacket(len, 1000);
        // 公司名称_产品型号_软件版本号_算法版本号_其他_产品用户[时间]
        if (ret == 0) {
            int length = len[0] - 7;
            byte[] data = new byte[length];
            System.arraycopy(revBuffer, 5, data, 0, length);
            int count = 0;
            for (int i = length - 1; i >= 0; i--) {
                if (data[i] == 0) count++;
                else break;
            }
            if (count > 0) {
                byte[] newData = new byte[length - count];
                System.arraycopy(data, 0, newData, 0, length - count);
                data = newData;
            }

            String version = new String(data);
            LogUtil.i(TAG,"维尔版本信息 = " + version);
            return version;
        }
        return null;
    }

    public int getFeature(int timeout, byte[] feature, int[] featureLen) {
        sendPacket(null, 0, (byte) 0x0C, (byte) (timeout / 1000), (byte) 0, (byte) 0);
        int[] len = new int[1];
        int ret = revPacket(len, 2000);
        featureLen[0] = len[0] - 7;
        if (ret == 0) {
            System.arraycopy(revBuffer, 5, feature, 0, featureLen[0]);
        }
        return ret;
    }

    public int checkFinger() {
        sendPacket(null, 0, (byte) 0x43, (byte) 0, (byte) 0, (byte) 0);

        int[] len = new int[1];
        int ret = revPacket(len, 2000);
        if (ret == 0) {
            // 0x0000 有手指； 0xffff 无手指
            ret = (revBuffer[5] == 0 && revBuffer[6] == 0) ? 0 : -1;
            LogUtil.i(TAG,"检测手指：ret = " + ret);
        }
        return ret;
    }

    public Pair<Integer, byte[]> getImage() {
        // P2 表示超时时间，0x01~0xFF，代表 1~255 秒
        // 0x00 表示以下位机设置的为准；
        // 0xXX 表示以下传的时间为准，范围为 1~255；
        // P3=0x00,黑底白纹，0x01,白底黑纹（保持与工行一致）
        sendPacket(null, 0, (byte) 0x18, (byte) 0, (byte) 0x01, (byte) 0);
        int[] len = new int[1];
        int ret = revPacket(len, 2000);
        if (ret == 0) {
            ret = revBuffer[3];
            if (ret != 0) return new Pair<>(ret, null);
            // 成功
            // 5-6 图像宽度，7-8 图像高度，
            int width = getLength(revBuffer[5], revBuffer[6]);
            int height = getLength(revBuffer[7], revBuffer[8]);

            // 上传指纹图像命令 0x19
            byte[] send = new byte[2];
            int size = width * height;
            LogUtil.i(TAG,"指纹图像 size = " + size);
            if (size == 0) {
                return new Pair<>(ErrorDefine.FINGERPRINT_IMAGE_ERROR, null);
            }
            // 发送指令前先切换波特率到 115200
            super.recycle();
            boolean b = initSerialPort(serialDev, baudRate2);
            if(!b){
                return new Pair<>(ErrorDefine.FINGERPRINT_IMAGE_ERROR, null);
            }
            int total = size / 1024 + ((size % 1024) > 0 ? 1 : 0);
            byte count = 0;
            byte[] data = new byte[size];
            int lastLen = 0;
            LogUtil.i(TAG,"图像数据总包数 " + total);
            while (!stopRun && total > count) {
                try {
                    LogUtil.i(TAG,"取图像的第 " + count +" 包数据");
                    send[1] = count++; // 不大于 0xFF 都没问题，否则要分成高低位
                    sendPacket(send, 2, (byte) 0x19, (byte) 0, (byte) 0x00, (byte) 0);
                    ret = revPacket(len, 2000);
                    if (len[0] <= 7) continue;
                    len[0] -= 7;
                    if (ret != 0) return new Pair<>(ret, null);
                    System.arraycopy(revBuffer, 5, data, lastLen, len[0]);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                lastLen += len[0];
            }
            return new Pair<>(ret, data);
        }
        return new Pair<>(ret, null);
    }

    private static int getLength(byte b1, byte b2) {
        return (b1 & 0xFF) * 256 + (b2 & 0xFF);
    }

    /**
     * @return 字 节  1    2   3    4   5  Len-2  Len+1 Len+2
     * 命 令 0x02 长度 长度 SW1 SW2 数据    校验   0x03
     * 字节数 1    1   1   1   1   不定长   1    1
     * <p>
     * SW1表示结果状态：
     * 0x00 //操作成功
     * 0x01 //操作失败
     * 0x04 //指纹数据库已满
     * 0x05 //无此用户
     * 0x07 //用户已存在
     * 0x08 //采集超时
     * 0x09 //空闲
     * 0x0A //命令执行中
     * 0x0B //有指纹按上
     * 0x0C //无指纹按上
     * 0x0D //指纹认证通过
     * 0x0E //指纹认证失败
     * 0x0F //处于安全等级 1
     * 0x10 //处于安全等级 2
     * 0x11 //处于安全等级 3
     * 0x12 //处于安全等级 4
     * 0x13 //处于安全等级 5
     * 0x20 //手指偏干
     * 0x21 //手指偏湿
     * 0x22 //位置太左
     * 0x23 //位置太右
     * 0x24 //位置太上
     * 0x25 //位置太下
     * 0x25 //面积太小
     * <p>
     * 校验位：CHK 校验和，为第 2 字节到数据字节的异或值
     */
    public int revPacket(int[] pwPacketetLen, int dwTimeoutMs) {
        Arrays.fill(revBuffer, (byte) 0x00);
        int wDataLen = 0;
        int byRet;
        // 接收协议头 0x02
        byRet = RecvBytes(revBuffer, 0, 1, dwTimeoutMs);
        if ((1 != byRet) || (2 != revBuffer[0])) {
            LogUtil.e(TAG,"收到的数据 1 = " + revBuffer[0]);
            flush();
            return -1;
        }

        // 接收协议数据长度位
        int srcLen = 2;
        byte[] data = recvSplitBytes(srcLen);
        if (null == data || data.length != srcLen) {
            LogUtil.e(TAG,"收到的数据 2 = " + ((null == data) ? "null" : ByteTool.byte2hex(data)));
            return ErrorDefine.RECV_PACKET_ERROR;
        }
        wDataLen = getLength(data[0], data[1]);
        if (wDataLen > 2176) {
            LogUtil.e(TAG,"收到的数据 > 2176");
            return -2511;
        }
        System.arraycopy(data, 0, revBuffer, 1, srcLen);

        // 接收后面所有的数据 - 1
        srcLen = wDataLen + 1;
        data = recvSplitBytes(srcLen);
        if (null == data || data.length != srcLen) {
            LogUtil.e(TAG,"收到的数据 3 = " + ((null == data) ? "null" : ByteTool.byte2hex(data)));
            return ErrorDefine.RECV_PACKET_ERROR;
        }
        System.arraycopy(data, 0, revBuffer, 3, srcLen);

        byRet = RecvBytes(revBuffer, 3 + srcLen, 1, 200);
        if (1 != byRet || revBuffer[4 + wDataLen] != 0x03) {
            LogUtil.e(TAG,"收到的结尾数据 != 0x03");
            return ErrorDefine.RECV_PACKET_ERROR;
        }
        LogUtil.e(TAG, "收到数据：" + ByteTool.byte2hex(revBuffer, 0, wDataLen + 5, false));
        pwPacketetLen[0] = wDataLen + 5;// 数据总长度：从协议头到协议尾
        return 0;
    }

    private byte[] recvSplitBytes(int srcLen) {
        int dataLen = srcLen * (isUseSplitData ? 2 : 1);
        byte[] data = new byte[dataLen];
        int byRet = RecvBytes(data, 0, dataLen, 200);
        //LogUtil.i(TAG,"收到的数据 data = " + ByteTool.byte2hex(data));
        if (dataLen != byRet) {
            return null;
        }
        if (isUseSplitData) {
            data = handleSplitData(data);
        }
        return data;
    }


    private byte[] handleSplitData(byte[] data) {
        String s = ByteTool.byte2hex(data);
        char[] chars = s.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i < chars.length; i = i + 2) {
            sb.append(chars[i]);
        }
        return ByteTool.hexStr2Bytes(sb.toString());
    }

    public static byte[] splitData(byte[] data) {
        int leng = data.length * 2;
        byte[] wellBuff = new byte[leng];
        for (int i = 0; i < data.length; i++) {
            wellBuff[i * 2] = (byte) (((data[i] >> 4) & 0x0F) + 0x30);
            wellBuff[i * 2 + 1] = (byte) ((data[i] & 0x0F) + 0x30);
        }
        return wellBuff;
        /*
          String s = ByteTool.byte2hex(sendBuffer, 1, sendBuffer.length - 1, false);
        byte[] bytes = new byte[s.length()];
        char[] chars = s.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            bytes[i] = (byte) (0x30 + Integer.parseInt(String.valueOf(chars[i]), 16));
        }
        byte[] data = new byte[2 + bytes.length];
        data[0] = HEADER;
        data[data.length - 1] = END;
        System.arraycopy(bytes, 0, data, 1, bytes.length);*/
    }

    public void sendPacket(byte[] send, int length, byte p1, byte p2, byte p3, byte p4) {
        byte[] sendBuffer = new byte[length + 7];
        int count = 0;
        sendBuffer[count++] = (byte) ((length + 4) / 256);
        sendBuffer[count++] = (byte) ((length + 4) % 256);
        sendBuffer[count++] = p1;
        sendBuffer[count++] = p2;
        sendBuffer[count++] = p3;
        sendBuffer[count++] = p4;
        if (null != send && send.length > 0)
            System.arraycopy(send, 0, sendBuffer, count, length);
        sendBuffer[length + count] = xor(sendBuffer, 0, sendBuffer.length - 1);

        byte[] data;
        // 拆分数据
        if (isUseSplitData) {
            byte[] splitData = splitData(sendBuffer);
            data = new byte[splitData.length + 2];
            System.arraycopy(splitData, 0, data, 1, splitData.length);
        } else {
            data = new byte[sendBuffer.length + 2];
            System.arraycopy(sendBuffer, 0, data, 1, sendBuffer.length);
        }
        data[0] = HEADER;
        data[data.length - 1] = END;

        LogUtil.e(TAG, "send:" + ByteTool.byte2hex(data));
        writeFlush(data, data.length);
    }

    public byte xor(byte[] b, int start, int end) {
        byte rs = (byte) ((b[start] & 0xFF) ^ (b[start + 1] & 0xFF));
        for (int i = start + 2; i < end; i++) {
            rs = (byte) ((rs & 0xFF) ^ (b[i] & 0xFF));
        }
        return rs;
    }

    @Override
    public void recycle() {
        setStopRun(true);
        if (isOpen) {
            flush();
            super.recycle();
            isOpen = false;
            powerOn(false);
        }
    }

}
